// Copyright 2009 Jan Hosang <jan.hosang@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package recipe

import (
    "container/list"
    "fmt"
    "os"
    
    "gb/tools"
    "gb/invocation"
)

type Recipe struct {
    Label string
    Targets, Sources *list.List
    Invocations *list.List
    IsLeaf bool
    vars *variables
}

type variables struct {
    targets, sources *list.List
    perc bool
    percRepl string
    vars map[string] *list.List
}

func New(label string, targets, sources, invocations *list.List) *Recipe {
    isLeaf := true
    for inv := range invocations.Iter() {
        if !inv.(interface{IsAtomic() bool}).IsAtomic() {
            isLeaf = false
            break
        }
    }
    return &Recipe{label, targets, sources, invocations, isLeaf, nil}
}

func (recp *Recipe) Header() string {
    return fmt.Sprintf("%s %s : %s",
                recp.Label,
                tools.ListToString(recp.Targets),
                tools.ListToString(recp.Sources))
}

func (recp *Recipe) String() string {
    return recp.Header()+"\n"+
        tools.ListToStringf(recp.Invocations, "    ", "\n    ", "\n")
}

func (recp *Recipe) SetVariables(targets *list.List, sources *list.List, perc bool, percRepl string, vars map[string] *list.List) {
    recp.vars = &variables{targets, sources, perc, percRepl, vars}
}

func (recp *Recipe) ReplaceVariables() *Recipe {
    if recp.vars == nil {
        fmt.Printf("Recipe.ReplaceVariables(): You cannot call without setting variables with SetVariables() before.\n")
        os.Exit(1)
    }
    
    tinv := invocation.New(recp.Label, recp.vars.targets, recp.vars.sources)
    tinv = tinv.ReplaceVariables(recp.vars.perc, recp.vars.percRepl, recp.vars.vars)
    
    recp.vars.vars["<"] = tinv.Sources
    recp.vars.vars[">"] = tinv.Targets
    
    invocations := list.New()
    for el := range recp.Invocations.Iter() {
        inv := el.(*invocation.Invocation)
        nInv := inv.ReplaceVariables(recp.vars.perc, recp.vars.percRepl, recp.vars.vars)
        invocations.PushBack(nInv)
    }
    
    return New(recp.Label, tinv.Targets, tinv.Sources, invocations)
}

func (recp *Recipe) Merge(orecp *Recipe) {
    if recp.vars == nil || orecp.vars == nil {
        fmt.Printf("Recipe.Merge(): You cannot call without setting variables with SetVariables() before.\n")
        os.Exit(1)
    }
    
    // targets should be the same, so we don't touch them
    for src := range orecp.vars.sources.Iter() {
        recp.vars.sources.PushBack(src)
    }
    for key, vals := range orecp.vars.vars {
        trgVals, ok := recp.vars.vars[key]
        if !ok {
            trgVals = list.New()
            recp.vars.vars[key] = trgVals
        }
        for val := range vals.Iter() {
            trgVals.PushBack(val)
        }
    }
}
